import 'dart:collection';

class MahjongUtil {
  static bool canHu(List<int> cards, int variable) {
    if ((cards.length + variable + 1) % 3 != 0) {
      return false;
    }
    cards.sort();
    if (cards.length + variable == 14 &&
        _checkPokerAA(List.from(cards), variable)) {
      return true;
    }
    for (int i = cards.length - 1, j = 0; i >= j; i--) {
      if (i < cards.length - 1 && cards[i] == cards[i + 1]) {
        continue;
      }
      if ((i >= 1 && cards[i] == cards[i - 1]) || variable > 0) {
        List<int> puCards = List.from(cards);
        int variableClone = variable;
        puCards.removeAt(i);
        if (i >= 1 && puCards[i - 1] == cards[i]) {
          puCards.removeAt(i - 1);
        } else {
          variableClone--;
        }
        if (_isPu(puCards, variableClone)) {
          return true;
        }
      }
    }
    if (variable >= 2 && _isPu(cards, variable - 2)) {
      return true;
    }
    return false;
  }

  static bool _isPu(List<int> cards, int variable) {
    if (cards.isEmpty) {
      return true;
    }
    for (int first = cards[0] - 2; first <= cards[0]; first++) {
      if (first % 10 > 7 || (variable == 0 && first < cards[0])) {
        continue;
      }
      int shunCount = 0;
      for (int i = 0; i < 3; i++) {
        if (cards.contains(first + i)) {
          shunCount++;
        }
      }
      if (shunCount == 3 || shunCount + variable >= 3) {
        List<int> puCards = List.from(cards);
        int variableClone = variable;
        for (int i = 0; i < 3; i++) {
          int deletePos = puCards.indexOf(first + i);
          if (deletePos >= 0) {
            puCards.removeAt(deletePos);
          } else {
            variableClone--;
          }
        }
        if (_isPu(puCards, variableClone)) {
          return true;
        }
      }
    }
    if (cards.length == 2 && cards[0] == cards[1] && variable == 1) {
      return true;
    } else if (cards.length == 2 && cards[0] != cards[1] && variable == 1) {
      return false;
    } else if (cards.length == 1 && variable == 2) {
      return true;
    } else if (cards.length == 1) {
      return false;
    }
    int keziCount = 1;
    int keziCard = cards[0];
    if (cards.length > 1 && cards[1] == keziCard) {
      keziCount++;
    }
    if (cards.length > 2 && cards[2] == keziCard) {
      keziCount++;
    }
    if (keziCount == 3 || keziCount + variable >= 3) {
      List<int> puCards = List.from(cards);
      int variableClone = variable;
      for (int i = 0; i < 3; i++) {
        int deletePos = puCards.indexOf(keziCard);
        if (deletePos >= 0) {
          puCards.removeAt(deletePos);
        } else {
          variableClone--;
        }
      }
      if (_isPu(puCards, variableClone)) {
        return true;
      }
    }
    return false;
  }

  static bool _checkPokerAA(List<int> list, int variable) {
    int totalCardCount = list.length + variable;
    if (totalCardCount % 2 != 0) {
      return false;
    }
    Map<int, int> countMap = HashMap();
    for (int card in list) {
      countMap[card] = (countMap[card] ?? 0) + 1;
    }
    int oddCount = 0;
    for (int count in countMap.values) {
      if (count % 2 != 0) {
        oddCount++;
      }
    }
    return oddCount <= variable;
  }

  static List<int> _checkHuCards(List<int> allCard, int variable) {
    List<int> allCardTemp = List.from(allCard);
    List<int> preSelections = [];
    int maxRedundant = (variable > 0) ? variable + 2 : 1;
    for (int card in allCardTemp) {
      int min = 1, max = 9;
      int maxRedundantTemp = maxRedundant;
      if (card > 10 && card < 20) {
        min = 11;
        max = 19;
      } else if (card > 20 && card < 30) {
        min = 20;
        max = 29;
      }
      int cardTemp = card;
      while (cardTemp >= min && maxRedundantTemp >= 0) {
        if (!preSelections.contains(cardTemp)) {
          preSelections.add(cardTemp);
        }
        cardTemp--;
        maxRedundantTemp--;
      }
      maxRedundantTemp = maxRedundant;
      cardTemp = card;
      while (cardTemp <= max && maxRedundantTemp >= 0) {
        if (!preSelections.contains(cardTemp)) {
          preSelections.add(cardTemp);
        }
        cardTemp++;
        maxRedundantTemp--;
      }
    }
    List<int> result = [];
    for (int element in preSelections) {
      List<int> tempList = List.from(allCard);
      tempList.add(element);
      if (canHu(tempList, variable)) {
        result.add(element);
      }
    }
    return result;
  }

  static List<CardEHInfo> checkHuCard(List<int> allCard, int variable) {
    List<CardEHInfo> result = [];
    int size = allCard.length + variable;
    int index = 0;
    int exportVariable = 0;
    if (size % 3 == 2) {
      while (index < size) {
        if (allCard.length > index) {
          List<int> allCardTemp = List.from(allCard);
          int exportCard = allCardTemp.removeAt(index);
          List<int> huCards = _checkHuCards(allCardTemp, variable);
          if (huCards.isNotEmpty) {
            result.add(CardEHInfo(exportCard, huCards));
          }
        } else {
          exportVariable++;
          List<int> allCardTemp = List.from(allCard);
          List<int> huCards =
              _checkHuCards(allCardTemp, variable - exportVariable);
          if (huCards.isNotEmpty) {
            result.add(CardEHInfo(1, huCards));
          }
        }
        index++;
      }
    } else if (size % 3 == 1) {
      List<int> huCards = _checkHuCards(List.from(allCard), variable);
      if (huCards.isNotEmpty) {
        result.add(CardEHInfo(null, huCards));
      }
    }
    return result;
  }

  static List<int> removeCard(List<int> userHandCard, List<int>? listCard) {
    List<int> cardsTemp = List.from(userHandCard);
    if (listCard == null) {
      return cardsTemp;
    }
    if (listCard.length == 0) {
      return cardsTemp;
    }

    for (int number in listCard) {
      int index = cardsTemp.indexOf(number);
      if (index != -1) {
        cardsTemp.removeAt(index);
      }
    }

    return cardsTemp;
  }
}

class CardEHInfo {
  int? exportCard;
  List<int> huCards;

  CardEHInfo(this.exportCard, this.huCards);

  @override
  String toString() {
    return "{出牌: $exportCard, 胡牌: $huCards}";
  }
}
